import anime from 'animejs'
import { edgeConfig } from '../config/edgeConfig'
import { clkStore } from '../config/runtimeConfig'
import { sizeConfig } from '../config/sizeConfig'
import { BoolType, IModuleWithState } from '../types'
import {
    $,
    binaryAt,
    createDataValue,
    getCenterPoint,
    setDomBoolVal,
    toBinaryStr,
} from '../utils'
import { bus1, bus3, bus4 } from './bus'
import { sm } from './sm'

interface IAuState {
    au_en: BoolType
    ac: number // 4位二进制
    au_gf: BoolType
}

interface IAu extends IModuleWithState<IAuState> {
    setAc: (value: number) => Promise<void>
}

// 给页面中的ac0、ac1、ac2、ac3设置点击事件
function setAcClickEvent() {
    const tempArr = ['ac0', 'ac1', 'ac2', 'ac3']
    tempArr.forEach((item, i) => {
        const dom = $(`.${item}`)
        dom?.addEventListener('click', () => {
            if (clkStore.isAnimating || clkStore.isRunning) {
                return
            }
            const ac = au.state.ac
            // 对ac的第i位取反
            const newAc = ac ^ (1 << i)
            au.setAc(newAc)
        })
    })
}

/**
 * au模块
 */
export const au: IAu = {
    name: 'au',
    state: {
        au_en: -1,
        ac: 0,
        au_gf: -1,
    },
    async setAc(value) {
        this.state.ac = value
        const ac0 = binaryAt(value, 0)
        const ac1 = binaryAt(value, 1)
        const ac2 = binaryAt(value, 2)
        const ac3 = binaryAt(value, 3)
        setDomBoolVal('ac0', ac0)
        setDomBoolVal('ac1', ac1)
        setDomBoolVal('ac2', ac2)
        setDomBoolVal('ac3', ac3)
        this.change!()
        return
    },
    async setState(prop, value) {
        if (this.state[prop] === value) return
        if (prop == 'ac') {
            await this.setAc(value)
        } else if (['au_en', 'au_gf'].includes(prop)) {
            this.state[prop] = value
            setDomBoolVal(prop, this.state[prop] as BoolType)
        }

        if (prop != 'au_gf' && prop != 'ac') {
            this.change!()
        }

        return
    },
    async change() {
        // 当en、ac、bus3、bus4发生变化时执行动画
        if (this.state.au_en == 0) {
            return
        }
        this.setState('au_gf', 0)
        const ac = this.state.ac
        // 加法，bus3 + bus4
        const aVal = bus3.getData() // a口的值，来自bus3
        const bVal = bus4.getData() // b口的值，来自bus4
        // au的中心点
        const auCenter = getCenterPoint(sizeConfig.au)
        // au的底部中心点
        const auBottomCenter = getCenterPoint(sizeConfig.au, 'bottom')
        // au的上部中心点
        const auTopCenter = getCenterPoint(sizeConfig.au, 'top')
        // a口的中心
        const aCenter = {
            x: auBottomCenter.x - sizeConfig.au.width / 4,
            y: auBottomCenter.y,
        }
        // b口的中心
        const bCenter = {
            x: auBottomCenter.x + sizeConfig.au.width / 4,
            y: auBottomCenter.y,
        }
        // 计算结果
        let result = aVal + bVal
        if (ac === 0b1000 || ac === 0b1001) {
            // a口要做动画的数据
            const aData = createDataValue(aVal, toBinaryStr(aVal), aCenter)
            // b口要做动画的数据
            const bData = createDataValue(bVal, toBinaryStr(bVal), bCenter)
            if (ac === 0b1001) {
                // 减法，bus4 - bus3
                result = bVal - aVal
                if (result < 0) {
                    result *= -1
                    this.setState('au_gf', 1)
                } else if (result > 255) {
                    result &= 0xff
                    this.setState('au_gf', 1)
                } else if (
                    binaryAt(bVal, 7) === binaryAt(aVal, 7) &&
                    bVal > aVal
                ) {
                    this.setState('au_gf', 1)
                }
            }
            // a口的动画
            const timeline1 = anime.timeline({
                targets: aData.dom,
                duration: (edgeConfig.time.neg + edgeConfig.time.low) * 0.15,
                easing: 'linear',
                complete: () => {
                    bus1.setData(result, 'au')
                    aData.dom.remove()
                },
            })
            timeline1
                .add({
                    translateX: auCenter.x,
                    translateY: auCenter.y - 10,
                    complete: () => {
                        timeline1.pause()
                    },
                })
                .add({
                    translateY: auTopCenter.y - 34 - 10,
                })

            // b口的动画
            const timeline2 = anime.timeline({
                targets: bData.dom,
                duration: (edgeConfig.time.neg + edgeConfig.time.low) * 0.15,
                easing: 'linear',
            })
            timeline2.add({
                translateX: auCenter.x,
                translateY: auCenter.y - 10,
                complete: () => {
                    bData.dom.remove()
                    aData.setText(toBinaryStr(result))
                    timeline1.play()
                },
            })
        } else if (ac === 0b0100 || ac === 0b0101 || ac === 0b1101) {
            // 将bus3的数据送到bus1
            // a口要做动画的数据
            const aData = createDataValue(aVal, toBinaryStr(aVal), aCenter)
            // a口的动画
            const timeline1 = anime.timeline({
                targets: aData.dom,
                duration: (edgeConfig.time.neg + edgeConfig.time.low) * 0.15,
                easing: 'linear',
                complete: () => {
                    bus1.setData(aVal, 'au')
                    aData.dom.remove()
                },
            })
            timeline1
                .add({
                    translateX: auTopCenter.x,
                    translateY: auTopCenter.y - 10,
                })
                .add({
                    translateY: auTopCenter.y - 34 - 10,
                })
        }
    },
    init() {
        this.setAc(0)
        this.setState('au_en', sm.state.sm_value)
        this.setState('au_gf', this.state.au_gf)
        setAcClickEvent()
        // this.setState('gf', 0)
    },
}
